﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Configuration.Provider;
using System.Data;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Security;

using Microsoft.SharePoint;
using System.Web;
using Kiiro.Common.Utility;
using Kiiro.Common.Entities;
using System.Net.Mail;
using Microsoft.SharePoint.Administration;

namespace Kiiro.Membership
{
    public class MembershipProvider : System.Web.Security.MembershipProvider
    {
        #region Constants
        const string MINIMUMPASSWORDLENGTH = "6";
        const string EVENTLOGSOURCE = "Kiiro Membership Provider";
        const string EVENTLOG = "Application";
        const string ERRORMESSAGE = "An exception occurred, causing the system to behave unexpectedly.  Please check the Application event log for more details.";
        #endregion

        #region EventLog Switch
        //
        // If false, exceptions are thrown to the caller. If true,
        // exceptions are written to the event log.
        //
        private bool pWriteExceptionsToEventLog;

        public bool WriteExceptionsToEventLog
        {
            get { return pWriteExceptionsToEventLog; }
            set { pWriteExceptionsToEventLog = value; }
        }
        #endregion

        #region System.Web.MembershipProvider Properties
        //
        // System.Web.Security.MembershipProvider properties.
        //
        private string pApplicationName;
        private bool pEnablePasswordReset;
        private bool pEnablePasswordRetrieval;
        private bool pRequiresQuestionAndAnswer;
        private bool pRequiresUniqueEmail;
        private int pMaxInvalidPasswordAttempts;
        private int pPasswordAttemptWindow;
        private MembershipPasswordFormat pPasswordFormat;

        public override string ApplicationName
        {
            get { return pApplicationName; }
            set { pApplicationName = value; }
        }

        public override bool EnablePasswordReset
        {
            get { return pEnablePasswordReset; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return pEnablePasswordRetrieval; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return pRequiresQuestionAndAnswer; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return pRequiresUniqueEmail; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return pMaxInvalidPasswordAttempts; }
        }

        public override int PasswordAttemptWindow
        {
            get { return pPasswordAttemptWindow; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return MembershipPasswordFormat.Hashed; }
            //get { return pPasswordFormat; }
        }

        private int pMinRequiredNonAlphanumericCharacters = 0;

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return pMinRequiredNonAlphanumericCharacters; }
        }

        private int pMinRequiredPasswordLength = 6;

        public override int MinRequiredPasswordLength
        {
            get { return pMinRequiredPasswordLength; }
        }

        private string pPasswordStrengthRegularExpression;

        public override string PasswordStrengthRegularExpression
        {
            get { return pPasswordStrengthRegularExpression; }
        }

        public override string Description
        {
            get
            {
                return base.Description;
            }
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
        }
        #endregion

        #region Custom MembershipProvider properties
        private Guid membershipListID;

        private string pMasterUserName;
        private string pMasterPassword;
        private string pMembershipListName;
        private string pUserFieldName;
        private string pPasswordFieldName;

        private SPList pMembershipList;
        private SPList pSetupList;
        private SPWeb mySite;
        private SPSite siteCollection;

        //
        // Used when determining encryption key values.
        //
        private MachineKeySection machineKey;


        public string MasterUserName
        {
            get { return pMasterUserName; }
        }

        public string MasterPassword
        {
            get { return pMasterPassword; }
        }

        public string ContactMembershipListName
        {
            get { return pMembershipListName; }
        }

        public SPList MembershipContactList
        {
            get { return pMembershipList; }
        }

        public string UserIDFieldName
        {
            get { return pUserFieldName; }
        }

        public string PasswordFieldName
        {
            get { return pPasswordFieldName; }
        }
        #endregion

        #region Helper functions

        /// <summary>
        /// A helper function to retrieve config values from the configuration file.
        /// </summary>
        /// <param name="configValue"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        private string GetConfigValue ( string configValue, string defaultValue )
        {
            if ( String.IsNullOrEmpty ( configValue ) )
                return defaultValue;

            return configValue;
        }

        /// <summary>
        /// Function creates new MembershipUser object based on values from an SPListItem
        /// </summary>
        /// <param name="userContact"></param>
        /// <returns></returns>
        private MembershipUser GetMembershipUserFromListItem ( SPListItem userContact )
        {

            //bool isApproved, isLockedOut;
            //DateTime creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockedOutDate;

            object providerUserKey = userContact.UniqueId;
            string username = userContact [ "User.Login" ].ToString ( );
            string email = userContact [ "User.Email" ].ToString ( );
            //string passwordQuestion = userContact.Fields.GetField("Password Question").GetFieldValueAsText(null);
            //string comment = userContact.Fields.GetField("Notes").GetFieldValueAsText(null);
            //bool.TryParse(userContact.GetFormattedValue("isApproved"), out isApproved);
            //bool.TryParse(userContact.GetFormattedValue("isLockedOut"), out isLockedOut);
            //DateTime.TryParse(userContact.GetFormattedValue("Creation Date"), out creationDate);
            //DateTime.TryParse(userContact.GetFormattedValue("lastLoginDate"), out lastLoginDate);
            //DateTime.TryParse(userContact.GetFormattedValue("lastActivityDate"), out lastActivityDate);
            //DateTime.TryParse(userContact.GetFormattedValue("lastPasswordChangedDate"), out lastPasswordChangedDate);
            //DateTime.TryParse(userContact.GetFormattedValue("lastLockedOutDate"), out lastLockedOutDate);

            MembershipUser u = new MembershipUser ( this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  "",//passwordQuestion,
                                                  "",//comment,
                                                  true,//isApproved,
                                                  false,//isLockedOut,
                                                  DateTime.Now.AddDays ( -30 ),//creationDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastLoginDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastActivityDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastPasswordChangedDate,
                                                  DateTime.Now.AddDays ( -1 ) );//lastLockedOutDate);



            return u;
        }

        /// <summary>
        /// Function creates new MembershipUserCollection based on values from a DataTable
        /// </summary>
        /// <param name="memberTable"></param>
        /// <returns></returns>
        private MembershipUserCollection GetMembershipUsersFromDataTable ( DataTable memberTable )
        {
            //doublecheck ordinal values vs column names
            for ( int i = 0; i < memberTable.Columns.Count; i++ )
            {
                System.Diagnostics.Debug.Print ( i.ToString ( ) + " - " + memberTable.Columns [ i ].ColumnName );
            }
            DataTableReader reader = new DataTableReader ( memberTable );
            MembershipUserCollection users = new MembershipUserCollection ( );

            while ( reader.Read ( ) )
            {
                MembershipUser u = GetMembershipUserFromDataReader ( reader );
                users.Add ( u );
            }
            reader.Close ( );
            reader.Dispose ( );

            return users;
        }

        private MembershipUser GetMembershipUserFromDataReader ( DataTableReader reader )
        {
            object providerUserKey = reader.GetValue ( ( int ) reader.GetOrdinal ( "ID" ) );
            string username = reader.GetString ( ( int ) reader.GetOrdinal ( "User.Login" ) );
            string email = "";
            if ( reader.GetValue ( ( int ) reader.GetOrdinal ( "User.Email" ) ) != DBNull.Value )
                email = reader.GetString ( ( int ) reader.GetOrdinal ( "User.Email" ) );
            //string passwordQuestion = "";

            MembershipUser u = new MembershipUser ( this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  "",//passwordQuestion,
                                                  "",//comment,
                                                  true,//isApproved,
                                                  false,//isLockedOut,
                                                  DateTime.Now.AddDays ( -30 ),//creationDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastLoginDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastActivityDate,
                                                  DateTime.Now.AddDays ( -1 ),//lastPasswordChangedDate,
                                                  DateTime.Now.AddDays ( -1 ) );//lastLockedOutDate);
            return u;
        }

        private MembershipUserCollection GetMembershipUsersFromDataTable ( DataView filteredMemberTable )
        {
            if ( filteredMemberTable.Table == null )
                return new MembershipUserCollection ( );

            //DataTable filterResults = filteredMemberTable.ToTable();
            return GetMembershipUsersFromDataTable ( filteredMemberTable.ToTable ( ) );
        }

        //
        // UpdateFailureCount
        //   A helper method that performs the checks and updates associated with
        // password failure tracking.
        //
        private void UpdateFailureCount ( string username, string failureType )
        {
            // not currently tracking failed password attempts
        }

        /// <summary>
        /// Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        private string EncodePassword ( string password )
        {
            string encodedPassword = password;

            switch ( PasswordFormat )
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodedPassword =
                      Convert.ToBase64String ( EncryptPassword ( Encoding.Unicode.GetBytes ( password ) ) );
                    break;
                case MembershipPasswordFormat.Hashed:
                    encodedPassword = HashPassword ( password );
                    break;
                default:
                    throw new ProviderException ( "Unsupported password format." );
            }

            return encodedPassword;
        }

        /// <summary>
        /// Decrypts or leaves the password clear based on the PasswordFormat.
        /// </summary>
        /// <param name="encodedPassword"></param>
        /// <returns></returns>
        private string UnEncodePassword ( string encodedPassword )
        {
            string password = encodedPassword;

            switch ( PasswordFormat )
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString ( DecryptPassword ( Convert.FromBase64String ( password ) ) );
                    break;
                case MembershipPasswordFormat.Hashed:
                    // can't unencode a hashed password, so return the hashed value of the plaintext password.
                    // password = HashPassword(encodedPassword);
                    break;

                default:
                    throw new ProviderException ( "Unsupported password format." );
            }

            return password;
        }

        /// <summary>
        /// Creates a base 64, hashed string of the password
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        internal string HashPassword ( string password )
        {
            HMACSHA1 hash = new HMACSHA1 ( );
            hash.Key = HexToByte ( Kiiro.Common.Utility.Constants.Security.HK );
            string encodedPassword =
              Convert.ToBase64String ( hash.ComputeHash ( Encoding.Unicode.GetBytes ( password ) ) );
            return encodedPassword;
        }

        /// <summary>
        /// Converts a hexadecimal string to a byte array. Used to convert encryption key values from the configuration.
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private byte [ ] HexToByte ( string hexString )
        {
            byte [ ] returnBytes = new byte [ hexString.Length / 2 ];
            for ( int i = 0; i < returnBytes.Length; i++ )
                returnBytes [ i ] = Convert.ToByte ( hexString.Substring ( i * 2, 2 ), 16 );
            return returnBytes;
        }

        /// <summary>
        /// Initializes the membership list
        /// </summary>
        /// <returns></returns>
        private SPList InitConnection ( )
        {   
            HttpRequest request = HttpContext.Current.Request;            
            Guid siteCollectionId = Guid.Empty;
            string serverRelativeUrl = null;

            string absUrl = request.Url.Scheme + "://" + request.Url.Authority + (
                request.Params [ "ctl00$PlaceHolderMain$idUrlSection$ctl02$TxtCreateSubwebName" ] ??
                request.QueryString [ "ReturnUrl" ] ??
                request.RawUrl );

            SPHelpers.GetSiteCollectionIdAndServerRelativeUrl ( absUrl, out siteCollectionId, out serverRelativeUrl );

            SPSecurity.RunWithElevatedPrivileges ( delegate ( )
            {
                try
                {
                    siteCollection = new SPSite ( siteCollectionId );
                    mySite = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl );
                    mySite.AllowUnsafeUpdates = true;
                    pMembershipList = mySite.Lists [ pMembershipListName ];
                    pSetupList = mySite.Lists [ "Setup" ];
                    membershipListID = pMembershipList.ID;
                }
                catch ( Exception ex )
                {
                    throw new Exception ( "The system failed to retrieve the membership list: " + ex.Message );
                }
            } );
            return pMembershipList;
        }

        private void DisposeConnection ( )
        {
            mySite.Dispose ( );
            siteCollection.Dispose ( );
            membershipListID = Guid.Empty;
        }
        #endregion

        #region Methods

        public override void Initialize ( string name, System.Collections.Specialized.NameValueCollection config )
        {
            try
            {

                SPSecurity.RunWithElevatedPrivileges ( delegate ( )
                {
                    // Initialize values from web.config.
                    if ( config == null )
                        throw new ArgumentNullException ( "config" );

                    if ( name == null || name.Length == 0 )
                        name = "KiiroMembershipProvider";

                    // Initialize the abstract base class.  
                    base.Initialize ( name, config );
                    // base provider required properties
                    pApplicationName = GetConfigValue ( config [ "applicationName" ], HostingEnvironment.ApplicationVirtualPath );
                    pMaxInvalidPasswordAttempts = Convert.ToInt32 ( GetConfigValue ( config [ "maxInvalidPasswordAttempts" ], "5" ) );
                    pPasswordAttemptWindow = Convert.ToInt32 ( GetConfigValue ( config [ "passwordAttemptWindow" ], "10" ) );
                    pMinRequiredNonAlphanumericCharacters = Convert.ToInt32 ( GetConfigValue ( config [ "minRequiredNonAlphanumericCharacters" ], "1" ) );
                    pMinRequiredPasswordLength = Convert.ToInt32 ( GetConfigValue ( config [ "minRequiredPasswordLength" ], MINIMUMPASSWORDLENGTH ) );
                    pPasswordStrengthRegularExpression = Convert.ToString ( GetConfigValue ( config [ "passwordStrengthRegularExpression" ], "" ) );
                    pEnablePasswordReset = Convert.ToBoolean ( GetConfigValue ( config [ "enablePasswordReset" ], "false" ) );
                    pEnablePasswordRetrieval = Convert.ToBoolean ( GetConfigValue ( config [ "enablePasswordRetrieval" ], "true" ) );
                    pRequiresQuestionAndAnswer = Convert.ToBoolean ( GetConfigValue ( config [ "requiresQuestionAndAnswer" ], "false" ) );
                    pRequiresUniqueEmail = Convert.ToBoolean ( GetConfigValue ( config [ "requiresUniqueEmail" ], "true" ) );
                    pWriteExceptionsToEventLog = Convert.ToBoolean ( GetConfigValue ( config [ "writeExceptionsToEventLog" ], "true" ) );
                    // custom provider properties
                    pMasterUserName = GetConfigValue ( config [ "masterUserName" ], "kiiroAdmin" );
                    pMasterPassword = GetConfigValue ( config [ "masterUserPassword" ], "kiiro" );
                    pMembershipListName = GetConfigValue ( config [ "MembershipList" ], "Users" );
                    pUserFieldName = GetConfigValue ( config [ "userIDFieldName" ], "User.Email" );
                    pPasswordFieldName = GetConfigValue ( config [ "passwordFieldName" ], "User.Password" );

                    string temp_format = config [ "passwordFormat" ];
                    if ( temp_format == null )
                        temp_format = "Hashed";

                    switch ( temp_format )
                    {
                        case "Hashed":
                            pPasswordFormat = MembershipPasswordFormat.Hashed;
                            break;
                        case "Encrypted":
                            pPasswordFormat = MembershipPasswordFormat.Encrypted;
                            break;
                        case "Clear":
                            pPasswordFormat = MembershipPasswordFormat.Clear;
                            break;
                        default:
                            throw new ProviderException ( "Password format not supported." );
                    }


                    // Get encryption and decryption key information from the configuration.
                    Configuration cfg = WebConfigurationManager.OpenWebConfiguration ( HostingEnvironment.ApplicationVirtualPath );
                    machineKey = ( MachineKeySection ) cfg.GetSection ( "system.web/machineKey" );

                    if ( machineKey.ValidationKey.Contains ( "AutoGenerate" ) )
                        if ( PasswordFormat != MembershipPasswordFormat.Clear )
                            throw new ProviderException ( "Hashed or Encrypted passwords are not supported with auto-generated keys." );
                } );
            }
            catch
            {
                base.Initialize ( name, config );
            }
        }

        public override bool ValidateUser ( string username, string password )
        {
            string encodedPassword = EncodePassword ( password );

            return CheckCredentials ( username, encodedPassword );
        }

        private bool CheckCredentials ( string username, string password )
        {
            SPQuery memberSearch = new SPQuery ( );

            memberSearch.Query = string.Format ( @"<Where><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq></Where>", 
                pUserFieldName, username );

            SPList membershipList = InitConnection ( );
            SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );
            DisposeConnection ( );

            bool crendentialsMatch = false;
            if ( validMembers != null && validMembers.Count > 0 )
            {
                SPListItem member = validMembers [ 0 ];
                if ( member.Properties [ pPasswordFieldName ] != null && ( string ) member.Properties [ pPasswordFieldName ] == password )
                    crendentialsMatch = true;
            }

            return crendentialsMatch;
        }

        public override MembershipUserCollection FindUsersByEmail ( string emailToMatch, int pageIndex, int pageSize, out int totalRecords )
        {
            SPList membershipList = InitConnection ( );
            string query = string.Format ( "<Where><Contains><FieldRef Name='User.Email' /><Value Type='Text'>{0}</Value></Contains></Where>", emailToMatch );
            SPQuery memberSearch = new SPQuery ( );
            memberSearch.Query = query;

            SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );
            MembershipUserCollection users = new MembershipUserCollection ( );

            if ( validMembers.Count > 0 )
            {
                DataTable memberTable = validMembers.GetDataTable ( );
                DataView filterUser = new DataView ( memberTable );
                filterUser.RowFilter = "User.Email LIKE '%" + emailToMatch + "%'";
                totalRecords = filterUser.Count;
                users = GetMembershipUsersFromDataTable ( filterUser );

                filterUser.Dispose ( );
                memberTable.Dispose ( );
            }
            else
            {
                totalRecords = 0;
            }

            DisposeConnection ( );
            return users;
        }

        public override MembershipUserCollection FindUsersByName ( string usernameToMatch, int pageIndex, int pageSize, out int totalRecords )
        {
            SPList membershipList = InitConnection ( );
            string query = string.Format ( "<Where><Contains><FieldRef Name='User.Login' /><Value Type='Text'>{0}</Value></Contains></Where>", usernameToMatch );
            SPQuery memberSearch = new SPQuery ( );
            memberSearch.Query = query;

            SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );
            MembershipUserCollection users = new MembershipUserCollection ( );
            if ( validMembers.Count > 0 )
            {
                DataTable memberTable = validMembers.GetDataTable ( );
                DataView filterUser = new DataView ( memberTable );
                filterUser.RowFilter = "User.Login LIKE '%" + usernameToMatch + "%'";
                totalRecords = filterUser.Count;
                users = GetMembershipUsersFromDataTable ( filterUser );

                filterUser.Dispose ( );
                memberTable.Dispose ( );
            }
            else
            {
                totalRecords = 0;
            }

            DisposeConnection ( );
            return users;
        }

        public override MembershipUserCollection GetAllUsers ( int pageIndex, int pageSize, out int totalRecords )
        {
            SPList membershipList = InitConnection ( );
            DataTable memberTable = new DataTable ( );
            try
            {
                totalRecords = membershipList.Items.Count;
                memberTable = membershipList.Items.GetDataTable ( );
                MembershipUserCollection users = GetMembershipUsersFromDataTable ( memberTable );

                memberTable.Dispose ( );

                DisposeConnection ( );
                return users;
            }
            catch ( Exception e )
            {
                memberTable.Dispose ( );

                DisposeConnection ( );
                throw new Exception ( "The method GetAllUsers threw an error: " + e.Message );
            }
        }

        public override MembershipUser GetUser ( object providerUserKey, bool userIsOnline )
        {
            SPList membershipList = InitConnection ( );

            try
            {
                DataTable memberTable = new DataTable ( );
                SPSecurity.RunWithElevatedPrivileges ( delegate ( )
                {
                    memberTable = membershipList.Items.GetDataTable ( );
                } );
                DataView filterUser = new DataView ( memberTable );
                filterUser.RowFilter = "ID = '" + providerUserKey.ToString ( ) + "'";

                if ( filterUser.Count == 1 )
                {
                    // found the user
                    DataTable filterTable = filterUser.ToTable ( );
                    DataTableReader filterReader = new DataTableReader ( filterTable );
                    filterReader.Read ( ); // read the first record
                    MembershipUser user = GetMembershipUserFromDataReader ( filterReader );

                    filterReader.Close ( );
                    filterReader.Dispose ( );
                    filterTable.Dispose ( );
                    filterUser.Dispose ( );
                    memberTable.Dispose ( );

                    DisposeConnection ( );
                    return user;
                }
                else
                {
                    DisposeConnection ( );
                    return null;
                }

            }
            catch
            {
                DisposeConnection ( );
                throw new Exception ( "The method GetUser(object, bool) threw an error attempting to search for the user " + providerUserKey.ToString ( ) );
            }
        }

        public override MembershipUser GetUser ( string username, bool userIsOnline )
        {
            SPList membershipList = InitConnection ( );

            try
            {
                SPQuery memberSearch = new SPQuery ( );
                memberSearch.Query = string.Format ( "<Where><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq></Where>", pUserFieldName, username );

                SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );

                if ( validMembers.Count == 1 )
                {
                    DataTable filterTable = validMembers.GetDataTable ( );
                    DataTableReader filterReader = new DataTableReader ( filterTable );
                    filterReader.Read ( );
                    MembershipUser user = GetMembershipUserFromDataReader ( filterReader );

                    filterReader.Close ( );
                    filterReader.Dispose ( );
                    filterTable.Dispose ( );

                    DisposeConnection ( );
                    return user;
                }
                else
                {
                    DisposeConnection ( );
                    return null;
                }
            }
            catch ( Exception e )
            {
                DisposeConnection ( );
                throw new Exception ( "The method GetUser(string, bool) encountered an error searching for " + username + ", Error: " + e.Message );
            }
        }

        public override string GetUserNameByEmail ( string email )
        {
            SPList membershipList = InitConnection ( );

            try
            {
                string query = string.Format ( "<Where><Eq><FieldRef Name='User.Email'/><Value Type='Text'>{0}</Value></Eq></Where>", email );
                SPQuery memberSearch = new SPQuery ( );
                memberSearch.Query = query;

                SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );

                if ( validMembers.Count == 1 )
                {
                    DataTable filterTable = validMembers.GetDataTable ( );
                    DataTableReader filterReader = new DataTableReader ( filterTable );
                    filterReader.Read ( );

                    DisposeConnection ( );

                    if ( filterReader.GetValue ( ( int ) filterReader.GetOrdinal ( "User.Login" ) ) == DBNull.Value )
                        return email;
                    else
                        return filterReader.GetString ( ( int ) filterReader.GetOrdinal ( "User.Login" ) );
                }
                else
                {
                    DisposeConnection ( );
                    return string.Empty;
                }
            }
            catch ( Exception e )
            {
                DisposeConnection ( );
                throw new Exception ( "The method GetUserNamebyEmail encountered an error searching for " + email + ", Error: " + e.Message );
            }
        }

        public override string ResetPassword ( string username, string answer )
        {
            string newPassword = System.Web.Security.Membership.GeneratePassword ( 8, 2 );

            SPQuery memberSearch = new SPQuery ( );

            memberSearch.Query = string.Format ( @"<Where><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq></Where>", 
                pUserFieldName, username );

            SPList membershipList = InitConnection ( );
            SPListItemCollection validMembers = membershipList.GetItems ( memberSearch );

            if ( validMembers != null && validMembers.Count > 0 )
            {
                SPListItem member = validMembers [ 0 ];

                SPQuery query = new SPQuery ( );
                query.RowLimit = 1;
                query.Query = "";
                SPListItemCollection listItems = pSetupList.GetItems ( query );

                SPListItem setupListItem = listItems [ 0 ];
                Setup setup = SPDataHelpers.MakeSetup ( setupListItem );

                if ( !string.IsNullOrEmpty ( setup.SMTPInfo ) && !string.IsNullOrEmpty ( setup.EmailFromAddress ) && setup.SendEmailUpdates )
                {

                    string encryptedPassword = ( HashPassword ( newPassword ) );
                    if ( member.Properties [ pPasswordFieldName ] == null )
                    {
                        member.Properties.Add ( pPasswordFieldName, encryptedPassword );
                    }
                    else
                    {
                        member.Properties [ pPasswordFieldName ] = encryptedPassword;
                    }

                    member.Update ( );
                    MembershipUser membershipUser = GetMembershipUserFromListItem ( member );

                    string formsPath = string.Empty;
                    SPWebApplication webApp = mySite.Site.WebApplication;
                    foreach ( SPAlternateUrl url in webApp.AlternateUrls )
                    {
                        if ( webApp.IisSettings.ContainsKey ( url.UrlZone ) && webApp.IisSettings [ url.UrlZone ] != null &&
                            !string.IsNullOrEmpty ( webApp.IisSettings [ url.UrlZone ].MembershipProvider ) &&
                            webApp.IisSettings [ url.UrlZone ].MembershipProvider == Constants.Permissions.FBAMembershipProviderName )
                        {
                            formsPath = url.Uri.ToString ( );
                        }
                    }

                    string [ ] args = new string [ ] {
                        membershipUser.UserName,
                        newPassword,
                        formsPath
                    };
                    MailMessage msg = new MailMessage ( setup.EmailFromAddress, membershipUser.Email, "Password Reset", GetPasswordResetBody ( args ) );
                    msg.IsBodyHtml = true;

                    SmtpClient mailClient = new SmtpClient ( setup.SMTPInfo );
                    mailClient.Send ( msg );
                }
                else
                {
                    throw new Exception ( "Outgoing emails are not configured, please contact the system administrator." );
                }
            }
            else
            {
                throw new Exception ( "Login not found." );
            }
            DisposeConnection ( );

            return newPassword;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="args">0 = User's name, 1 = new password, 2 = midori url</param>
        /// <returns></returns>
        private string GetPasswordResetBody ( string [ ] args )
        {
            StringBuilder sb = new StringBuilder ( );
            sb.Append ( "<p>Hi {0},</p>" );
            sb.Append ( "<p>You password has been reset.</p>" );
            sb.Append ( "<p>Your new password is: {1}</p>" );
            sb.Append ( "<p>Login to Kiiro here: <a href='{2}'>{2}</a></p>" );

            if ( args.Length >= 2 )
                return string.Format ( sb.ToString ( ), args [ 0 ], args [ 1 ], args [ 2 ] );

            return default ( string );
        }

        #endregion

        #region Not Implemented

        protected override byte [ ] DecryptPassword ( byte [ ] encodedPassword )
        {
            return base.DecryptPassword ( encodedPassword );
        }

        protected override byte [ ] EncryptPassword ( byte [ ] password )
        {
            return base.EncryptPassword ( password );
        }

        public override string GetPassword ( string username, string answer )
        {
            throw new Exception ( "The method or operation GetPassword is not implemented." );
        }

        protected override void OnValidatingPassword ( ValidatePasswordEventArgs e )
        {
            base.OnValidatingPassword ( e );
        }

        public override bool UnlockUser ( string userName )
        {
            throw new Exception ( "The method or operation UnlockUser is not implemented." );
        }

        public override void UpdateUser ( MembershipUser user )
        {
            throw new Exception ( "The method or operation UpdateUser is not implemented." );
        }

        public override bool ChangePasswordQuestionAndAnswer ( string username, string password, string newPasswordQuestion, string newPasswordAnswer )
        {
            throw new Exception ( "The method or operation ChangePasswordQuestionAndAnswer is deliberately not implemented in this provider as the functionality is expected to be handled in the SharePoint list." );
        }

        public override bool DeleteUser ( string username, bool deleteAllRelatedData )
        {
            throw new Exception ( "The method or operation DeleteUser is deliberately not implemented in this provider as the functionality is expected to be handled in the SharePoint list." );
        }

        public override int GetNumberOfUsersOnline ( )
        {
            throw new Exception ( "The method or operation GetNumberOfUsersOnline is not implemented." );
        }

        public override bool ChangePassword ( string username, string oldPassword, string newPassword )
        {
            throw new Exception ( "The method or operation ChangePassword is not implemented." );
        }

        public override MembershipUser CreateUser ( string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status )
        {
            throw new Exception ( "The method or operation CreateUser is not implemented." );
        }

        #endregion
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.Membership/MembershipProvider.cs $
 * 
 * 12    9/11/09 6:21p Camerons
 * 
 * 11    2/11/09 7:16p Camerons
 * 
 * 10    13/10/09 12:39p Camerons
 * 
 * 9     13/10/09 12:04p Camerons
 * 
 * 8     5/10/09 12:12p Stefanf
 * 
 * 7     4/09/09 6:11p Camerons
 * 
 * 2     4/09/09 6:04p Camerons
 * 
 * 5     17/08/09 10:45a Camerons
 * Added check-in comments section
 * 
 ************************************************************************/